using System;
using System.Collections;
using System.Linq;
using System.Reflection;
using NodeCanvas.Variables;
using UnityEngine;

namespace NodeCanvas
{
	public abstract class Task : MonoBehaviour
	{
		[AttributeUsage(AttributeTargets.Class)]
		protected class AgentTypeAttribute : Attribute
		{
			public Type type;

			public AgentTypeAttribute(Type type)
			{
				this.type = type;
			}
		}

		[AttributeUsage(AttributeTargets.Class)]
		protected class EventListenerAttribute : Attribute
		{
			public string[] messages;

			public EventListenerAttribute(params string[] args)
			{
				messages = args;
			}
		}

		[AttributeUsage(AttributeTargets.Field)]
		protected class GetFromAgentAttribute : Attribute
		{
		}

		[Serializable]
		private class TaskAgent : BBVariable
		{
			[SerializeField]
			private bool _isOverride;

			[SerializeField]
			private Component _value;

			public bool isOverride
			{
				get
				{
					return _isOverride;
				}
				set
				{
					_isOverride = value;
				}
			}

			public Component current { get; set; }

			public Component value
			{
				get
				{
					if (base.useBlackboard)
					{
						UnityEngine.Object @object = Read<UnityEngine.Object>();
						if (@object != null)
						{
							if (@object is GameObject)
							{
								return (@object as GameObject).transform;
							}
							if (@object is Component)
							{
								return (Component)@object;
							}
						}
						return null;
					}
					return _value;
				}
				set
				{
					_value = value;
				}
			}

			public override Type varType
			{
				get
				{
					return typeof(UnityEngine.Object);
				}
			}

			public override string ToString()
			{
				if (!isOverride)
				{
					return "<b>owner</b>";
				}
				if (base.useBlackboard)
				{
					return base.ToString();
				}
				return string.Format("<b>{0}</b>", (!(_value != null)) ? "NULL" : _value.name);
			}
		}

		[SerializeField]
		private MonoBehaviour _ownerSystem;

		[SerializeField]
		private bool _isActive = true;

		[SerializeField]
		private TaskAgent taskAgent = new TaskAgent();

		private Blackboard _blackboard;

		private Type _agentType;

		private string _taskName;

		private string _taskDescription;

		public ITaskSystem ownerSystem
		{
			get
			{
				return _ownerSystem as ITaskSystem;
			}
		}

		private Component ownerAgent
		{
			get
			{
				return (ownerSystem == null) ? null : ownerSystem.agent;
			}
		}

		private Blackboard ownerBlackboard
		{
			get
			{
				return (ownerSystem == null) ? null : ownerSystem.blackboard;
			}
		}

		protected float ownerElapsedTime
		{
			get
			{
				return (ownerSystem == null) ? 0f : ownerSystem.elapsedTime;
			}
		}

		public new string name
		{
			get
			{
				return taskName;
			}
		}

		public bool isActive
		{
			get
			{
				return _isActive;
			}
			set
			{
				_isActive = value;
			}
		}

		public string taskName
		{
			get
			{
				if (string.IsNullOrEmpty(_taskName))
				{
					NameAttribute nameAttribute = GetType().NCGetAttribute(typeof(NameAttribute), false) as NameAttribute;
					_taskName = ((nameAttribute == null) ? GetType().Name : nameAttribute.name);
				}
				return _taskName;
			}
		}

		public string taskDescription
		{
			get
			{
				if (_taskDescription == null)
				{
					DescriptionAttribute descriptionAttribute = GetType().NCGetAttribute(typeof(DescriptionAttribute), true) as DescriptionAttribute;
					_taskDescription = ((descriptionAttribute == null) ? string.Empty : descriptionAttribute.description);
				}
				return _taskDescription;
			}
		}

		public Type agentType
		{
			get
			{
				if (_agentType == null)
				{
					AgentTypeAttribute agentTypeAttribute = GetType().NCGetAttribute(typeof(AgentTypeAttribute), true) as AgentTypeAttribute;
					_agentType = ((agentTypeAttribute == null || (!typeof(Component).NCIsAssignableFrom(agentTypeAttribute.type) && !agentTypeAttribute.type.NCIsInterface())) ? typeof(Component) : agentTypeAttribute.type);
				}
				return _agentType;
			}
		}

		public virtual string summaryInfo
		{
			get
			{
				return taskName;
			}
		}

		public string agentInfo
		{
			get
			{
				return taskAgent.ToString();
			}
		}

		public bool agentIsOverride
		{
			get
			{
				return taskAgent.isOverride;
			}
			private set
			{
				if (!value && taskAgent.isOverride)
				{
					taskAgent.useBlackboard = false;
					taskAgent.value = null;
				}
				taskAgent.isOverride = value;
			}
		}

		protected Component agent
		{
			get
			{
				return taskAgent.current ? taskAgent.current : ((!agentIsOverride) ? ownerAgent : taskAgent.value);
			}
		}

		protected Blackboard blackboard
		{
			get
			{
				if (_blackboard == null)
				{
					_blackboard = ownerBlackboard;
					UpdateBBFields(_blackboard);
				}
				return _blackboard;
			}
			private set
			{
				if (_blackboard != value)
				{
					UpdateBBFields(value);
				}
				_blackboard = value;
			}
		}

		public void SetOwnerSystem(ITaskSystem newOwnerSystem)
		{
			if (newOwnerSystem != null)
			{
				_ownerSystem = (MonoBehaviour)newOwnerSystem;
				UpdateBBFields(newOwnerSystem.blackboard);
			}
		}

		protected void Awake()
		{
			MonoManager.Create();
			base.enabled = false;
			InitBBFields();
			OnAwake();
		}

		protected virtual void OnAwake()
		{
		}

		protected new Coroutine StartCoroutine(IEnumerator routine)
		{
			return MonoManager.current.StartCoroutine(routine);
		}

		protected void SendEvent(string eventName)
		{
			if (ownerSystem != null)
			{
				ownerSystem.SendEvent(eventName);
			}
		}

		protected virtual string OnInit()
		{
			return null;
		}

		protected bool Set(Component newAgent, Blackboard newBB)
		{
			blackboard = newBB;
			if (agentIsOverride)
			{
				if ((bool)taskAgent.current && (bool)taskAgent.value && taskAgent.current.gameObject == taskAgent.value.gameObject)
				{
					return true;
				}
				return Initialize(TransformAgent(taskAgent.value, agentType));
			}
			if ((bool)taskAgent.current && taskAgent.current.gameObject == newAgent.gameObject)
			{
				return true;
			}
			return Initialize(TransformAgent(newAgent, agentType));
		}

		private Component TransformAgent(Component newAgent, Type type)
		{
			return (type == typeof(Component) || !(newAgent != null)) ? newAgent : newAgent.GetComponent(type);
		}

		private bool Initialize(Component newAgent)
		{
			taskAgent.current = newAgent;
			if (newAgent == null)
			{
				Debug.LogError(string.Concat("<b>Task Init:</b> Failed to change Agent to type '", agentType, "', for Task '", taskName, "' or new Agent is NULL. Does the Agent has that Component?"), this);
				return false;
			}
			SubscribeToEvents(newAgent);
			FieldInfo[] array = GetType().NCGetFields();
			foreach (FieldInfo fieldInfo in array)
			{
				object value = fieldInfo.GetValue(this);
				RequiredFieldAttribute requiredFieldAttribute = fieldInfo.GetCustomAttributes(typeof(RequiredFieldAttribute), true).FirstOrDefault() as RequiredFieldAttribute;
				if (requiredFieldAttribute != null)
				{
					if (value == null || value.Equals(null))
					{
						Debug.LogError("<b>Task Init:</b> A required field for Task '" + taskName + "', is not set! Field: '" + fieldInfo.Name + "' ", this);
						return false;
					}
					if (fieldInfo.FieldType == typeof(string) && string.IsNullOrEmpty((string)value))
					{
						Debug.LogError("<b>Task Init:</b> A required string for Task '" + taskName + "', is not set! Field: '" + fieldInfo.Name + "' ", this);
						return false;
					}
					if (typeof(BBVariable).NCIsAssignableFrom(fieldInfo.FieldType) && (value as BBVariable).isNull)
					{
						Debug.LogError("<b>Task Init:</b> A required BBVariable value for Task '" + taskName + "', is not set! Field: '" + fieldInfo.Name + "' ", this);
						return false;
					}
				}
				GetFromAgentAttribute getFromAgentAttribute = fieldInfo.GetCustomAttributes(typeof(GetFromAgentAttribute), true).FirstOrDefault() as GetFromAgentAttribute;
				if (getFromAgentAttribute != null)
				{
					if (!typeof(Component).NCIsAssignableFrom(fieldInfo.FieldType))
					{
						Debug.LogError("<b>Task Init:</b> You've set a GetFromAgent Attribute on a field (" + fieldInfo.Name + ") whos type does not derive Component on Task '" + taskName + "'", this);
						return false;
					}
					fieldInfo.SetValue(this, newAgent.GetComponent(fieldInfo.FieldType));
					if (fieldInfo.GetValue(this) as UnityEngine.Object == null)
					{
						Debug.LogError(string.Concat("<b>Task Init:</b> GetFromAgent Attribute failed to get the required component of type '", fieldInfo.FieldType, "' from '", agent.gameObject.name, "'. Does it exist?"), agent.gameObject);
						return false;
					}
				}
				if (fieldInfo.FieldType == typeof(BBGameObject))
				{
					RequiresComponentAttribute requiresComponentAttribute = fieldInfo.GetCustomAttributes(typeof(RequiresComponentAttribute), true).FirstOrDefault() as RequiresComponentAttribute;
					BBGameObject bBGameObject = value as BBGameObject;
					if (requiresComponentAttribute != null && !bBGameObject.isNull && bBGameObject.value.GetComponent(requiresComponentAttribute.type) == null)
					{
						Debug.LogError(string.Concat("<b>Task Init:</b> BBGameObject requires missing Component of type '", requiresComponentAttribute.type, "'."), agent.gameObject);
						return false;
					}
				}
			}
			string text = OnInit();
			if (text != null)
			{
				Debug.LogError("<b>Task Init:</b> " + text + ". Task '" + taskName + "'");
				return false;
			}
			return true;
		}

		private void SubscribeToEvents(Component newAgent)
		{
			EventListenerAttribute eventListenerAttribute = GetType().NCGetAttribute(typeof(EventListenerAttribute), true) as EventListenerAttribute;
			if (eventListenerAttribute != null)
			{
				AgentUtilities agentUtilities = newAgent.GetComponent<AgentUtilities>();
				if (agentUtilities == null)
				{
					agentUtilities = newAgent.gameObject.AddComponent<AgentUtilities>();
				}
				string[] messages = eventListenerAttribute.messages;
				foreach (string toMessage in messages)
				{
					agentUtilities.Listen(this, toMessage);
				}
			}
		}

		private void UnsubscribeFromEvents()
		{
			AgentUtilities component = agent.GetComponent<AgentUtilities>();
			if (component != null)
			{
				component.Forget(this);
			}
		}

		private void UpdateBBFields(Blackboard bb)
		{
			BBVariable.SetBBFields(bb, this);
			taskAgent.bb = bb;
		}

		private void InitBBFields()
		{
			BBVariable.InitBBFields(this);
		}

		public sealed override string ToString()
		{
			return string.Format("{0} ({1})", taskName, summaryInfo);
		}

		public void DrawGizmos()
		{
			OnGizmos();
		}

		public void DrawGizmosSelected()
		{
			OnGizmosSelected();
		}

		protected virtual void OnGizmos()
		{
		}

		protected virtual void OnGizmosSelected()
		{
		}
	}
}
